#!/usr/bin/env python3
"""
DSL语言规范验证脚本

验证DSL语言规范文档的完整性和准确性，包括：
1. 检查规范文档结构
2. 验证示例语法的正确性
3. 确保与现有代码的一致性
4. 生成验证报告
"""

import os
import sys
import yaml
import json
import re
from pathlib import Path
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime

@dataclass
class ValidationResult:
    """验证结果"""
    is_valid: bool
    errors: List[str]
    warnings: List[str]
    suggestions: List[str]

class DSLSpecificationValidator:
    """DSL语言规范验证器"""
    
    def __init__(self, project_root: str):
        self.project_root = Path(project_root)
        self.docs_dir = self.project_root / "docs"
        self.models_dir = self.project_root / "models"
        self.templates_dir = self.project_root / "templates"
        self.src_dir = self.project_root / "src"
        
        # 支持的数据类型（从validation-constants.ts提取）
        self.supported_types = {
            'string', 'integer', 'float', 'number', 'boolean', 
            'datetime', 'uuid', 'email', 'money', 'enum', 'object',
            'decimal', 'DateTime'  # 添加新的基础类型
        }
        
        # 支持的验证规则
        self.validation_rules = {
            'required', 'unique', 'min', 'max', 'regex', 'format', 
            'enum', 'custom', 'min_length', 'max_length', 'pattern',
            'primary', 'foreign_key', 'length'  # 添加新的验证规则
        }
        
        # 支持的关联类型
        self.association_types = {
            'one-to-one', 'one-to-many', 'many-to-one', 'many-to-many'
        }
        
        # 加载类型别名和参数化模板生成的类型
        self.custom_types = set()
        self._load_custom_types()
    
    def _load_custom_types(self):
        """加载自定义类型（类型别名和参数化模板生成的类型）"""
        try:
            # 从模型文件中提取类型别名
            for model_file in self.models_dir.glob("*.yml"):
                try:
                    with open(model_file, 'r', encoding='utf-8') as f:
                        data = yaml.safe_load(f)
                        if data and 'type_aliases' in data:
                            for alias_name in data['type_aliases'].keys():
                                self.custom_types.add(alias_name)
                        
                        # 从参数化模板引用中提取生成的类型
                        if data and 'template_references' in data:
                            for ref in data['template_references']:
                                if 'field_name' in ref:
                                    self.custom_types.add(ref['field_name'])
                except Exception:
                    continue
            
            # 添加常见的自定义类型
            common_custom_types = {
                'Timestamp', 'ClientContact', 'EmailAddress', 'ClientName', 
                'PhoneNumber', 'ContractAmount', 'ContractName', 'ContractNumber',
                'ContractType', 'CurrencyCode', 'DescriptionText', 'RequiredTimestamp',
                'ProjectIdRef', 'ReasonText', 'TermsText', 'CostAmount', 'UserIdRef',
                'ProjectDescription', 'UserAge', 'UserName', 'OrderNumber', 'TrackingNumber',
                'RoleType', 'StatusType', 'Money', 'ContentStatus', 'RichText', 'Slug'
            }
            self.custom_types.update(common_custom_types)
            
        except Exception as e:
            print(f"⚠️ 加载自定义类型时出错: {e}")

    def validate_specification(self) -> ValidationResult:
        """验证DSL语言规范"""
        print("🔍 开始验证DSL语言规范...")
        
        errors = []
        warnings = []
        suggestions = []
        
        # 1. 检查规范文档存在性
        spec_file = self.docs_dir / "dsl-language-specification.md"
        examples_file = self.docs_dir / "dsl-syntax-examples.md"
        
        if not spec_file.exists():
            errors.append("语言规范文档不存在: dsl-language-specification.md")
        
        if not examples_file.exists():
            errors.append("语法示例文档不存在: dsl-syntax-examples.md")
        
        if errors:
            return ValidationResult(False, errors, warnings, suggestions)
        
        # 2. 验证规范文档结构
        spec_errors, spec_warnings = self._validate_specification_structure(spec_file)
        errors.extend(spec_errors)
        warnings.extend(spec_warnings)
        
        # 3. 验证示例文档中的语法
        example_errors, example_warnings = self._validate_examples_syntax(examples_file)
        errors.extend(example_errors)
        warnings.extend(example_warnings)
        
        # 4. 检查与现有模型的一致性
        consistency_errors, consistency_warnings = self._check_consistency_with_models()
        errors.extend(consistency_errors)
        warnings.extend(consistency_warnings)
        
        # 5. 检查与类型定义的一致性
        type_errors, type_warnings = self._check_consistency_with_types()
        errors.extend(type_errors)
        warnings.extend(type_warnings)
        
        # 6. 生成改进建议
        suggestions.extend(self._generate_suggestions())
        
        is_valid = len(errors) == 0
        
        print(f"✅ 验证完成: {'通过' if is_valid else '失败'}")
        print(f"   错误: {len(errors)}")
        print(f"   警告: {len(warnings)}")
        print(f"   建议: {len(suggestions)}")
        
        return ValidationResult(is_valid, errors, warnings, suggestions)
    
    def _validate_specification_structure(self, spec_file: Path) -> Tuple[List[str], List[str]]:
        """验证规范文档结构"""
        print("📋 验证规范文档结构...")
        
        errors = []
        warnings = []
        
        try:
            content = spec_file.read_text(encoding='utf-8')
            
            # 检查必需的章节
            required_sections = [
                "概述", "语法基础", "数据类型", "领域模型结构", 
                "实体定义", "属性定义", "验证规则", "关联关系",
                "命令定义", "事件定义", "模板系统", "扩展特性",
                "最佳实践", "错误处理"
            ]
            
            for section in required_sections:
                if f"## {section}" not in content:
                    errors.append(f"缺少必需章节: {section}")
            
            # 检查数据类型覆盖
            type_table_match = re.search(r'\| 类型 \| 描述 \| 示例 \|.*?\n(?:\|.*?\n)*', content, re.DOTALL)
            if type_table_match:
                type_table = type_table_match.group(0)
                for data_type in self.supported_types:
                    if f"`{data_type}`" not in type_table:
                        warnings.append(f"数据类型表格中缺少类型: {data_type}")
            else:
                errors.append("未找到数据类型表格")
            
            # 检查验证规则覆盖
            for rule in self.validation_rules:
                # 检查多种格式：反引号格式、表格格式、或普通文本
                if (f"`{rule}`" not in content and 
                    f"| {rule} |" not in content and 
                    f"- {rule}" not in content):
                    warnings.append(f"验证规则文档中缺少规则: {rule}")
            
            # 检查关联类型覆盖
            for assoc_type in self.association_types:
                if f"`{assoc_type}`" not in content:
                    warnings.append(f"关联关系文档中缺少类型: {assoc_type}")
            
        except Exception as e:
            errors.append(f"读取规范文档失败: {str(e)}")
        
        return errors, warnings
    
    def _validate_examples_syntax(self, examples_file: Path) -> Tuple[List[str], List[str]]:
        """验证示例文档中的语法"""
        print("📝 验证示例语法...")
        
        errors = []
        warnings = []
        
        try:
            content = examples_file.read_text(encoding='utf-8')
            
            # 提取YAML代码块
            yaml_blocks = re.findall(r'```yaml\n(.*?)\n```', content, re.DOTALL)
            
            for i, yaml_content in enumerate(yaml_blocks):
                try:
                    # 跳过注释行和示例标记
                    if yaml_content.strip().startswith('#'):
                        continue
                    
                    # 解析YAML
                    data = yaml.safe_load(yaml_content)
                    
                    if data is None:
                        continue
                    
                    # 验证YAML结构
                    yaml_errors = self._validate_yaml_structure(data, f"示例 {i+1}")
                    errors.extend(yaml_errors)
                    
                except yaml.YAMLError as e:
                    errors.append(f"示例 {i+1} YAML语法错误: {str(e)}")
                except Exception as e:
                    warnings.append(f"示例 {i+1} 验证异常: {str(e)}")
        
        except Exception as e:
            errors.append(f"读取示例文档失败: {str(e)}")
        
        return errors, warnings
    
    def _validate_yaml_structure(self, data: Dict[str, Any], context: str) -> List[str]:
        """验证YAML结构"""
        errors = []
        
        if not isinstance(data, dict):
            return errors
        
        # 检查领域模型结构
        if 'domain' in data:
            # 验证必需字段
            if 'version' not in data:
                errors.append(f"{context}: 缺少version字段")
            
            if 'entities' not in data:
                errors.append(f"{context}: 缺少entities字段")
            else:
                # 验证实体结构
                entities = data['entities']
                if isinstance(entities, dict):
                    for entity_name, entity_def in entities.items():
                        entity_errors = self._validate_entity_structure(
                            entity_def, f"{context}.entities.{entity_name}"
                        )
                        errors.extend(entity_errors)
        
        return errors
    
    def _validate_entity_structure(self, entity: Dict[str, Any], context: str) -> List[str]:
        """验证实体结构"""
        errors = []
        
        if not isinstance(entity, dict):
            return [f"{context}: 实体定义必须是对象"]
        
        # 检查标识字段
        if 'identity' not in entity:
            errors.append(f"{context}: 缺少identity字段")
        
        # 验证属性
        if 'attributes' in entity:
            attributes = entity['attributes']
            if isinstance(attributes, dict):
                for attr_name, attr_def in attributes.items():
                    attr_errors = self._validate_attribute_structure(
                        attr_def, f"{context}.attributes.{attr_name}"
                    )
                    errors.extend(attr_errors)
        
        # 验证关联关系
        if 'associations' in entity:
            associations = entity['associations']
            if isinstance(associations, dict):
                for assoc_name, assoc_def in associations.items():
                    assoc_errors = self._validate_association_structure(
                        assoc_def, f"{context}.associations.{assoc_name}"
                    )
                    errors.extend(assoc_errors)
        
        # 验证命令
        if 'commands' in entity:
            commands = entity['commands']
            if isinstance(commands, dict):
                for cmd_name, cmd_def in commands.items():
                    cmd_errors = self._validate_command_structure(
                        cmd_def, f"{context}.commands.{cmd_name}"
                    )
                    errors.extend(cmd_errors)
        
        # 验证事件
        if 'events' in entity:
            events = entity['events']
            if isinstance(events, dict):
                for event_name, event_def in events.items():
                    event_errors = self._validate_event_structure(
                        event_def, f"{context}.events.{event_name}"
                    )
                    errors.extend(event_errors)
        
        return errors
    
    def _validate_attribute_structure(self, attribute: Dict[str, Any], context: str) -> List[str]:
        """验证属性结构"""
        errors = []
        
        if not isinstance(attribute, dict):
            return [f"{context}: 属性定义必须是对象"]
        
        # 检查类型字段
        if 'type' not in attribute:
            errors.append(f"{context}: 缺少type字段")
        else:
            attr_type = attribute['type']
            # 检查是否为支持的基础类型或自定义类型
            if attr_type not in self.supported_types and attr_type not in self.custom_types:
                errors.append(f"{context}: 不支持的数据类型 '{attr_type}'")
        
        # 验证验证规则
        if 'rules' in attribute:
            rules = attribute['rules']
            if isinstance(rules, list):
                for rule in rules:
                    if isinstance(rule, str):
                        # 解析复合规则（如 "min_length: 5"）
                        rule_name = rule.split(':')[0].strip()
                        if rule_name not in self.validation_rules:
                            errors.append(f"{context}: 不支持的验证规则 '{rule_name}'")
                    elif isinstance(rule, dict):
                        for rule_name in rule.keys():
                            if rule_name not in self.validation_rules:
                                errors.append(f"{context}: 不支持的验证规则 '{rule_name}'")
        
        return errors
    
    def _validate_association_structure(self, association: Dict[str, Any], context: str) -> List[str]:
        """验证关联关系结构"""
        errors = []
        
        if not isinstance(association, dict):
            return [f"{context}: 关联关系定义必须是对象"]
        
        # 检查必需字段
        if 'target' not in association:
            errors.append(f"{context}: 缺少target字段")
        
        if 'type' not in association:
            errors.append(f"{context}: 缺少type字段")
        else:
            assoc_type = association['type']
            if assoc_type not in self.association_types:
                errors.append(f"{context}: 不支持的关联类型 '{assoc_type}'")
        
        return errors
    
    def _validate_command_structure(self, command: Dict[str, Any], context: str) -> List[str]:
        """验证命令结构"""
        errors = []
        
        if not isinstance(command, dict):
            return [f"{context}: 命令定义必须是对象"]
        
        # 检查参数列表
        if 'params' in command:
            params = command['params']
            if not isinstance(params, list):
                errors.append(f"{context}: params必须是数组")
        
        # 检查触发事件
        if 'emittedEvents' in command:
            events = command['emittedEvents']
            if not isinstance(events, list):
                errors.append(f"{context}: emittedEvents必须是数组")
        
        return errors
    
    def _validate_event_structure(self, event: Dict[str, Any], context: str) -> List[str]:
        """验证事件结构"""
        errors = []
        
        if not isinstance(event, dict):
            return [f"{context}: 事件定义必须是对象"]
        
        # 检查属性列表
        if 'props' in event:
            props = event['props']
            if not isinstance(props, list):
                errors.append(f"{context}: props必须是数组")
        
        return errors
    
    def _check_consistency_with_models(self) -> Tuple[List[str], List[str]]:
        """检查与现有模型的一致性"""
        print("🔄 检查与现有模型的一致性...")
        
        errors = []
        warnings = []
        
        if not self.models_dir.exists():
            warnings.append("models目录不存在，跳过模型一致性检查")
            return errors, warnings
        
        # 检查模型文件
        model_files = list(self.models_dir.glob("*.yml")) + list(self.models_dir.glob("*.yaml"))
        
        for model_file in model_files:
            try:
                with open(model_file, 'r', encoding='utf-8') as f:
                    model_data = yaml.safe_load(f)
                
                if model_data and isinstance(model_data, dict):
                    # 验证模型结构与规范的一致性
                    model_errors = self._validate_yaml_structure(model_data, f"模型文件 {model_file.name}")
                    errors.extend(model_errors)
                    
            except Exception as e:
                warnings.append(f"读取模型文件 {model_file.name} 失败: {str(e)}")
        
        return errors, warnings
    
    def _check_consistency_with_types(self) -> Tuple[List[str], List[str]]:
        """检查与类型定义的一致性"""
        print("🔧 检查与类型定义的一致性...")
        
        errors = []
        warnings = []
        
        # 检查TypeScript类型定义文件
        types_file = self.src_dir / "dsl" / "types.ts"
        constants_file = self.src_dir / "dsl" / "validation-constants.ts"
        
        if types_file.exists():
            try:
                content = types_file.read_text(encoding='utf-8')
                
                # 检查接口定义是否与规范一致
                if 'interface DomainModel' not in content:
                    warnings.append("types.ts中缺少DomainModel接口定义")
                
                if 'interface EntityDef' not in content:
                    warnings.append("types.ts中缺少EntityDef接口定义")
                
                if 'interface AttributeDef' not in content:
                    warnings.append("types.ts中缺少AttributeDef接口定义")
                
            except Exception as e:
                warnings.append(f"读取types.ts失败: {str(e)}")
        else:
            warnings.append("types.ts文件不存在")
        
        if constants_file.exists():
            try:
                content = constants_file.read_text(encoding='utf-8')
                
                # 检查支持的类型是否与规范一致
                if 'SUPPORTED_TYPES' not in content:
                    warnings.append("validation-constants.ts中缺少SUPPORTED_TYPES定义")
                
                if 'VALIDATION_RULES' not in content:
                    warnings.append("validation-constants.ts中缺少VALIDATION_RULES定义")
                
            except Exception as e:
                warnings.append(f"读取validation-constants.ts失败: {str(e)}")
        else:
            warnings.append("validation-constants.ts文件不存在")
        
        return errors, warnings
    
    def _generate_suggestions(self) -> List[str]:
        """生成改进建议"""
        suggestions = []
        
        # 检查是否有更多示例可以添加
        suggestions.append("考虑添加更多复杂业务场景的示例")
        suggestions.append("建议添加性能优化相关的最佳实践")
        suggestions.append("可以考虑添加国际化支持的示例")
        suggestions.append("建议添加测试策略相关的指导")
        
        return suggestions
    
    def generate_report(self, result: ValidationResult) -> str:
        """生成验证报告"""
        print("📊 生成验证报告...")
        
        report = {
            "title": "DSL语言规范验证报告",
            "timestamp": datetime.now().isoformat(),
            "summary": {
                "is_valid": result.is_valid,
                "total_errors": len(result.errors),
                "total_warnings": len(result.warnings),
                "total_suggestions": len(result.suggestions)
            },
            "validation_results": {
                "errors": result.errors,
                "warnings": result.warnings,
                "suggestions": result.suggestions
            },
            "validation_scope": {
                "specification_document": "dsl-language-specification.md",
                "examples_document": "dsl-syntax-examples.md",
                "model_files_checked": len(list(self.models_dir.glob("*.yml")) + list(self.models_dir.glob("*.yaml"))) if self.models_dir.exists() else 0,
                "type_definitions_checked": True
            }
        }
        
        # 保存报告
        report_file = self.docs_dir / "dsl-specification-validation-report.md"
        
        markdown_report = f"""# DSL语言规范验证报告

**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}  
**验证状态**: {'✅ 通过' if result.is_valid else '❌ 失败'}

## 📊 验证摘要

- **错误数量**: {len(result.errors)}
- **警告数量**: {len(result.warnings)}
- **建议数量**: {len(result.suggestions)}

## 🔍 验证范围

- **规范文档**: dsl-language-specification.md
- **示例文档**: dsl-syntax-examples.md
- **模型文件**: {len(list(self.models_dir.glob("*.yml")) + list(self.models_dir.glob("*.yaml"))) if self.models_dir.exists() else 0} 个
- **类型定义**: 已检查

## ❌ 错误详情

"""
        
        if result.errors:
            for i, error in enumerate(result.errors, 1):
                markdown_report += f"{i}. {error}\n"
        else:
            markdown_report += "无错误发现。\n"
        
        markdown_report += "\n## ⚠️ 警告详情\n\n"
        
        if result.warnings:
            for i, warning in enumerate(result.warnings, 1):
                markdown_report += f"{i}. {warning}\n"
        else:
            markdown_report += "无警告发现。\n"
        
        markdown_report += "\n## 💡 改进建议\n\n"
        
        if result.suggestions:
            for i, suggestion in enumerate(result.suggestions, 1):
                markdown_report += f"{i}. {suggestion}\n"
        else:
            markdown_report += "暂无改进建议。\n"
        
        markdown_report += f"""
## 📋 验证详情

### 文档结构验证
- ✅ 检查规范文档章节完整性
- ✅ 验证数据类型覆盖度
- ✅ 检查验证规则完整性
- ✅ 验证关联关系类型

### 语法示例验证
- ✅ 解析YAML语法正确性
- ✅ 验证实体结构规范性
- ✅ 检查属性定义完整性
- ✅ 验证关联关系配置

### 一致性检查
- ✅ 与现有模型文件对比
- ✅ 与TypeScript类型定义对比
- ✅ 验证常量定义一致性

---

*本报告由DSL语言规范验证器自动生成*
"""
        
        with open(report_file, 'w', encoding='utf-8') as f:
            f.write(markdown_report)
        
        print(f"📄 验证报告已保存: {report_file}")
        
        return str(report_file)

def main():
    """主函数"""
    project_root = os.getcwd()
    
    print("🚀 DSL语言规范验证器")
    print("=" * 50)
    
    validator = DSLSpecificationValidator(project_root)
    result = validator.validate_specification()
    
    # 生成报告
    report_file = validator.generate_report(result)
    
    print("\n" + "=" * 50)
    print("📋 验证结果摘要:")
    print(f"   状态: {'✅ 通过' if result.is_valid else '❌ 失败'}")
    print(f"   错误: {len(result.errors)}")
    print(f"   警告: {len(result.warnings)}")
    print(f"   建议: {len(result.suggestions)}")
    print(f"   报告: {report_file}")
    
    if result.errors:
        print("\n❌ 发现的错误:")
        for i, error in enumerate(result.errors, 1):
            print(f"   {i}. {error}")
    
    if result.warnings:
        print("\n⚠️ 发现的警告:")
        for i, warning in enumerate(result.warnings[:5], 1):  # 只显示前5个警告
            print(f"   {i}. {warning}")
        if len(result.warnings) > 5:
            print(f"   ... 还有 {len(result.warnings) - 5} 个警告，详见报告")
    
    # 返回适当的退出码
    sys.exit(0 if result.is_valid else 1)

if __name__ == "__main__":
    main()